package main;

import java.io.File;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import model.Balise;
import model.Document;
import model.Mot;

import org.tartarus.snowball.ext.FrenchStemmer;

import parsing.DocumentParser;
import query.QueryUtility;
import query.evaluator.QueryEvaluator;
import query.io.QueriesIO;
import query.model.QueryWord;
import query.model.UserQuery;
import stemming.SnowBallStemmer;
import utils.SparqlClient;
import database.SQLHelper;

/**
 * Classe de lancement de l'application
 */
public class RunApp
{
  
  private static SparqlClient client = null;
  
  /**
   * Méthode de lancement de l'application
   * 
   * @param args
   */
  public static void main(String[] args)
  {
    // Remise à zero de la base
    long start0 = System.currentTimeMillis();
    razDatabase();
    long stop0 = System.currentTimeMillis();

    System.out.println("1 - Empty database : " + (stop0 - start0) + "ms");

    // Remplir la BDD avec les documents
    long start1 = System.currentTimeMillis();
    fillDatabaseWithFiles();
    long stop1 = System.currentTimeMillis();

    System.out.println("2 - Fill database (documents) : " + (stop1 - start1) + "ms");

    // On parse les fichiers contenus dans la BDD
    // On récupère une liste de Document contenant tous les éléments
    long start2 = System.currentTimeMillis();
    List<Document> parsedDocuments = parseDocuments();
    long stop2 = System.currentTimeMillis();

    System.out.println("3 - Parse documents : " + (stop2 - start2) + "ms");

    // On passe le contenu des documents au lemmatiseur
    // On récupère la liste des documents avec le contenu lemmatisé au lieu des
    // mots du document
    long start3 = System.currentTimeMillis();
    List<Document> lemmatisedDocuments = lemmatiseDocuments(parsedDocuments);
    long stop3 = System.currentTimeMillis();

    System.out.println("4 - Lemmatise documents : " + (stop3 - start3) + "ms");

    // On insère ces éléments dans la base
    List<Document> documentsInserted = fillDatabaseWithDocuments(lemmatisedDocuments);

    // On lit le fichier contenant les queries
    List<UserQuery> queries = readQueries();

    // On trouve les balises pertinentes et on évalue chaque query
    QueryUtility utility = new QueryUtility(documentsInserted);
    for (UserQuery userQuery : queries)
    {
      // Ajout des mots avec sparql
      addSynonymsWithSparql(userQuery);
      addSubClassWithSparql(userQuery);

      // 2nd paramètre = nombre de balises pertinentes à retourner
      List<Balise> releventBalises = utility.getReleventBalises(userQuery, 25);
      
      QueryEvaluator comparator = new QueryEvaluator(releventBalises, userQuery);
      comparator.compare();
    }
  }

  /**
   * Do a SPARQL request to find the subclasses of the words in the query 
   * using our ontology
   *
   * @param userQuery the user query
   */
  private static void addSubClassWithSparql(UserQuery userQuery) {
  
    if(client == null) {
      client = new SparqlClient("localhost:3030/space");
    }

    List<QueryWord> wordsToAdd = new ArrayList<>();

    SnowBallStemmer stemmer = new SnowBallStemmer(new FrenchStemmer());
    
    for (QueryWord word : userQuery.getQueryWords())
    {
      String query = "PREFIX : <http://ontologies.alwaysdata.net/space#>\n"
          + "PREFIX rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n"
          + "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n"
          + "PREFIX owl:  <http://www.w3.org/2002/07/owl#>\n"
          + "PREFIX xsd:  <http://www.w3.org/2001/XMLSchema#>\n" 
          + "SELECT *\n" + "WHERE {\n"
          + "  ?x rdfs:label ?label.\n" 
          + "  ?son rdfs:subClassOf ?x.\n"
          + "  ?son rdfs:label ?labelSon.\n"
          + "  FILTER regex(?label, \""
          + word.getWord()
          + "\", \"i\")\n" + "}\n";

      Iterable<Map<String, String>> results = client.select(query);
      for (Map<String, String> result : results)
      {
        String subClassTerm = stemmer.stemAWord(result.get("labelSon"));
        QueryWord newWord = new QueryWord(userQuery.getQueryWords().size(), subClassTerm, 1.0/result.size());
        if(!userQuery.getQueryWords().contains(newWord) && !wordsToAdd.contains(newWord))
        {
          wordsToAdd.add(newWord);
        }
      }
    }
    
    for (QueryWord word : wordsToAdd)
    {
      userQuery.addQueryWord(word);
    }
  }
  
  /**
   * Do a SPARQL request to find the synonyms of the words in the query 
   * using our ontology
   *
   * @param userQuery the user query
   */
  private static void addSynonymsWithSparql(UserQuery userQuery)
  {
    
    if(client == null) {
      client = new SparqlClient("localhost:3030/space");
    }

    List<QueryWord> wordsToAdd = new ArrayList<>();

    SnowBallStemmer stemmer = new SnowBallStemmer(new FrenchStemmer());
    for (QueryWord word : userQuery.getQueryWords())
    {
      String query = "PREFIX : <http://ontologies.alwaysdata.net/space#>\n"
          + "PREFIX rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n"
          + "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n"
          + "PREFIX owl:  <http://www.w3.org/2002/07/owl#>\n"
          + "PREFIX xsd:  <http://www.w3.org/2001/XMLSchema#>\n" 
          + "SELECT *\n" + "WHERE {\n"
          + "  ?x rdfs:label ?label.\n" 
          + "  ?x rdfs:label ?synonyme.\n"
          + "  FILTER regex(?label, \""
          + word.getWord()
          + "\", \"i\")\n" + "}\n";

      Iterable<Map<String, String>> results = client.select(query);
      for (Map<String, String> result : results)
      {
        String synonyme = stemmer.stemAWord(result.get("synonyme"));
        QueryWord newWord = new QueryWord(userQuery.getQueryWords().size(), synonyme, 2.0/result.size());
        if(!userQuery.getQueryWords().contains(newWord) && !wordsToAdd.contains(newWord))
        {
          wordsToAdd.add(newWord);
        }
      }
    }
    
    for (QueryWord word : wordsToAdd)
    {
      userQuery.addQueryWord(word);
    }
  }
  
  /**
   * Reads the query written by the user
   * 
   * @return the string
   */
  public static List<UserQuery> readQueries()
  {
    File file = new File(ISpecificPath.QUERIES_PATH);
    QueriesIO io = new QueriesIO();
    List<UserQuery> queries = io.readQueriesFromFile(file);
    return queries;
  }

  /**
   * Vide tout le contenu de la base
   */
  private static void razDatabase()
  {
    SQLHelper.emptyDatabase();
  }

  /**
   * Cette méthode se place dans le dossier contenant tous les documents XML à
   * indexer et les ajoute à la base de données
   */
  private static void fillDatabaseWithFiles()
  {
    List<File> xmlFiles = new ArrayList<>();
    File folder = new File(ISpecificPath.COLLECTION_PATH);

    for (File file : folder.listFiles())
    {
      String path = file.getAbsolutePath();
      // Si c'est un fichier XML on l'ajoute aux fichiers à insérer
      if (path.substring(path.length() - 3, path.length()).equals("xml"))
      {
        xmlFiles.add(file);
      }
    }

    // On insère les fichiers dans la BDD
    SQLHelper.insertFilesInDatabase(xmlFiles);
  }

  /**
   * Récupère tous les documents référencés dans la base de données et parse
   * leur contenu pour remplir le modèle
   * 
   * @return liste des documents et leur contenu
   */
  private static List<Document> parseDocuments()
  {
    return DocumentParser.parse();
  }

  /**
   * Prends en entrée les documents avec les mots bruts. Produit en sortie les
   * documents avec le contenu lemmatisé
   * 
   * @param parsedDocuments les documents parsés
   * @return les documents lemmatisés
   */
  private static List<Document> lemmatiseDocuments(List<Document> parsedDocuments)
  {
    SnowBallStemmer stemmer = new SnowBallStemmer(new FrenchStemmer());
    List<Document> documentsWithStemmedTerms = new ArrayList<>();

    for (Document document : parsedDocuments)
    {
      List<Balise> newBalises = new ArrayList<>();
      for (Balise balise : document.getBalises())
      {
        List<Mot> newTerms = new ArrayList<>();
        for (Mot terme : balise.getMots())
        {
          newTerms.add(new Mot(stemmer.stemAWord(terme.getValue())));
        }
        newBalises.add(new Balise(balise.getId(), balise.getNom(), balise.getChemin(), balise
            .getDocument(), newTerms, balise.getBaliseNumber()));
      }
      documentsWithStemmedTerms.add(new Document(document.getId(), document.getName(), newBalises));
    }

    return documentsWithStemmedTerms;
  }

  /**
   * Insère les éléments du document dans la base de données
   * 
   * @param lemmatisedDocuments les documents avec le contenu lemmatisé
   * @return
   * @throws SQLException
   */
  private static List<Document> fillDatabaseWithDocuments(List<Document> lemmatisedDocuments)
  {
    return SQLHelper.insertDocumentsContent(lemmatisedDocuments);
  }
}
